/**
 * @file lv_watch_obj.c
 *
 */

/*********************
 *      INCLUDES
 *********************/
#include "lv_watch.h"

#if USE_LV_WATCH_OBJ != 0

#include <stdio.h>
#include <stdlib.h>

/*********************
 *      DEFINES
 *********************/

/**********************
 *      TYPEDEFS
 **********************/
#define LV_WATCH_OBJ_ANIMATION_TIME 50//200
#define LV_WATCH_OBJ_HIDE_THRESHOLD 80

/**********************
 *  STATIC PROTOTYPES
 **********************/
static void lv_watch_obj_pressed_handler(lv_obj_t * obj, lv_obj_t * element);
static void lv_watch_obj_pressing_handler(lv_obj_t * obj, lv_obj_t * element);
static void lv_watch_obj_press_lost_handler(lv_obj_t * obj, lv_obj_t * element);
static void lv_watch_obj_hide_anim_end(lv_anim_t * a);
static lv_res_t element_signal_func(lv_obj_t * element, lv_signal_t sign, void * param);
static void lv_watch_obj_prepare_destory(lv_obj_t * activity_obj);
static lv_watch_obj_element_free_ptr_client_t * element_get_ext_client(lv_obj_t * element);
static void lv_watch_obj_add_element_with_special(lv_obj_t * element, lv_watch_obj_element_free_ptr_client_t * attribute);
static void set_default_anim_callback(lv_obj_t * obj);

/**********************
 *  STATIC VARIABLES
 **********************/

/**********************
 *      MACROS
 **********************/

/**********************
 *   GLOBAL FUNCTIONS
***********************/
/*
lv_obj_t * lv_watch_actitvity_obj_create(lv_obj_t * par)
{
    (void)par;

    lv_watch_activity_ext_t activity_ext;
    memset(&activity_ext, 0, sizeof(lv_watch_activity_ext_t));
    activity_ext.actId = ACT_ID_ANYOBJ;
    activity_ext.create = NULL;
    activity_ext.prepare_destory = NULL;
    lv_obj_t * obj = lv_watch_creat_activity_obj(&activity_ext);
    lv_mem_assert(obj);
    if(obj == NULL) return NULL;
    lv_watch_obj_create(obj);
    return obj;
}
*/

lv_obj_t * lv_watch_obj_create(lv_obj_t * activity_obj)
{
    if(activity_obj == NULL) {
        lv_watch_activity_ext_t activity_ext;
        memset(&activity_ext, 0, sizeof(lv_watch_activity_ext_t));
        activity_ext.actId = ACT_ID_ANYOBJ;
        activity_ext.create = NULL;
        activity_ext.prepare_destory = NULL;
        activity_obj = lv_watch_creat_activity_obj(&activity_ext);
        LV_ASSERT_MEM(activity_obj);
        if(activity_obj == NULL) return NULL;
    }

    lv_obj_t  * obj;
    if(WATCH_HAS_WALLPAPER) {
        lv_obj_t * img = lv_img_create(activity_obj, NULL);
        LV_ASSERT_MEM(img);
        if(img == NULL) return NULL;
        lv_img_set_src(img, watch_get_bg());
        lv_obj_set_size(img, LV_HOR_RES, LV_VER_RES);

        obj = lv_obj_create(img, NULL);
    } else {
        lv_obj_t * par_obj = lv_obj_create(activity_obj, NULL);
        lv_obj_set_size(par_obj, LV_HOR_RES, LV_VER_RES);
        lv_obj_set_style_local_bg_color(par_obj, LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_BLACK);
        obj = lv_obj_create(par_obj, NULL);
    }

    lv_obj_set_size(obj, LV_HOR_RES, LV_VER_RES);
    /* use common background */
    lv_obj_add_style(obj, LV_OBJ_PART_MAIN, &lv_style_transp);

    /*Allocate the object type specific extended data*/
    lv_watch_obj_ext_t * ext = lv_obj_allocate_ext_attr(obj, sizeof(lv_watch_obj_ext_t));
    LV_ASSERT_MEM(ext);
    if(ext == NULL) return NULL;
    lv_obj_set_size(obj, LV_HOR_RES, LV_VER_RES);
    memset(ext, 0, sizeof(lv_watch_obj_ext_t));

    ext->activity_obj = activity_obj;
    ext->point_last.x = 0;
    ext->point_last.y = 0;
    ext->point_start.x = 0;
    ext->point_start.y = 0;
    ext->point_now.x = 0;
    ext->point_now.y = 0;
    ext->hor_drag_start = 5;
    ext->ver_drag_start = 5;
    ext->anim_time = LV_WATCH_OBJ_ANIMATION_TIME;
    ext->anim_mode = LV_WATCH_ANIM_HOR_RIGHT_HIDE;
    ext->slide_enable = 1;
    ext->draging = 0;
    ext->drag_hor = 0;
    ext->element_click_en = 1;
    ext->is_drag_right = 1;
    ext->is_drag_up = 1;

    set_default_anim_callback(obj);

//    lv_watch_obj_set_free_ptr(obj, lv_obj_get_signal_cb(obj));
    lv_obj_set_signal_cb(obj, lv_watch_obj_signal);

    lv_obj_set_drag(obj, false);

    /* If obj is hiding, Since lv_input.c indev_proc_press() will search new obj to do press signal handler,
     * So the backgroud obj may be dragged or background's button is clicked.
     * To avoid it, need protect LV_PROTECT_PRESS_LOST for the obj.
     */
    lv_obj_add_protect(obj, LV_PROTECT_PRESS_LOST | LV_PROTECT_CLICK_FOCUS);

    return obj;
}

void lv_watch_obj_reset_anim_cb(lv_obj_t * obj)
{
    lv_watch_obj_set_anim_cb(obj, SLIDE_LEFT_PRESSING_CB, NULL);
    lv_watch_obj_set_anim_cb(obj, SLIDE_LEFT_PRESS_LOST_CB, lv_watch_obj_recover_element_clicked_en);
    lv_watch_obj_set_anim_cb(obj, SLIDE_UP_PRESSING_CB, NULL);
    lv_watch_obj_set_anim_cb(obj, SLIDE_UP_PRESS_LOST_CB, lv_watch_obj_recover_element_clicked_en);
    lv_watch_obj_set_anim_cb(obj, SLIDE_DOWN_PRESSING_CB, NULL);
    lv_watch_obj_set_anim_cb(obj, SLIDE_DOWN_PRESS_LOST_CB, lv_watch_obj_recover_element_clicked_en);
    lv_watch_obj_set_anim_cb(obj, SLIDE_RIGHT_PRESSING_CB, NULL);
    lv_watch_obj_set_anim_cb(obj, SLIDE_RIGHT_PRESS_LOST_CB, lv_watch_obj_recover_element_clicked_en);
}

void lv_watch_obj_set_free_ptr(lv_obj_t * obj, void * free_p)
{
    lv_watch_obj_ext_t * ext = lv_obj_get_ext_attr(obj);
    ext->free_ptr = free_p;
}

void * lv_watch_obj_get_free_ptr(const lv_obj_t * obj)
{
    lv_watch_obj_ext_t * ext = lv_obj_get_ext_attr(obj);
    return ext->free_ptr;
}

lv_res_t lv_watch_obj_set_anim_mode(lv_obj_t * obj, lv_watch_anim_mode_t anim_mode)
{
    lv_watch_obj_ext_t * ext = lv_obj_get_ext_attr(obj);
    if(ext == NULL) return LV_RES_INV;
    ext->anim_mode = anim_mode;
    set_default_anim_callback(obj);
    return LV_RES_OK;
}

lv_res_t lv_watch_obj_set_slide_enable(lv_obj_t * obj, bool slide_enable)
{
    lv_watch_obj_ext_t * ext = lv_obj_get_ext_attr(obj);
    if(ext == NULL) return LV_RES_INV;
    ext->slide_enable = slide_enable;
    return LV_RES_OK;
}

void lv_watch_obj_add_element(lv_obj_t * element)
{
    lv_watch_obj_add_element_with_special(element, NULL);
}

void lv_watch_obj_add_element_with_special(lv_obj_t * element, lv_watch_obj_element_free_ptr_client_t * attribute)
{
    lv_watch_set_free_ptr(element, lv_obj_get_signal_cb(element));
    lv_watch_set_client_ptr(element, attribute);
    lv_obj_set_signal_cb(element, element_signal_func);
    lv_obj_add_protect(element, LV_PROTECT_PRESS_LOST | LV_PROTECT_CLICK_FOCUS);
}

void lv_watch_obj_cleanup(lv_obj_t * element)
{
    if(element == NULL) return;

    /*Get the obj from the element*/
    lv_obj_t * obj = lv_obj_get_parent(element);
    while(obj) {
        if(lv_obj_get_signal_cb(obj) != lv_watch_obj_signal) obj = lv_obj_get_parent(obj);
        else break;
    }

    if(obj) {
        lv_watch_obj_ext_t * ext = lv_obj_get_ext_attr(obj);
        if(ext != NULL) {
            if(ext->activity_obj) {
                lv_watch_obj_prepare_destory(ext->activity_obj);
                lv_obj_del(ext->activity_obj);
            }
            return;
        }
        lv_obj_del(obj);
    }
}

void lv_watch_obj_set_anim_cb(lv_obj_t * obj, lv_watch_anim_cb_type_t cb_type, void (*cb)(lv_obj_t *))
{
    lv_watch_obj_ext_t * ext = lv_obj_get_ext_attr(obj);
    if(ext == NULL) return;

    switch(cb_type) {
        case SLIDE_LEFT_PRESSING_CB:
            ext->slide_left_pressing_cb = (void(*)(void *))cb;
            break;
        case SLIDE_LEFT_PRESS_LOST_CB:
            ext->slide_left_press_lost_cb = (void(*)(void *))cb;
            break;
        case SLIDE_RIGHT_PRESSING_CB:
            ext->slide_right_pressing_cb = (void(*)(void *))cb;
            break;
        case SLIDE_RIGHT_PRESS_LOST_CB:
            ext->slide_right_press_lost_cb = (void(*)(void *))cb;
            break;
        case SLIDE_UP_PRESSING_CB:
            ext->slide_up_pressing_cb = (void(*)(void *))cb;
            break;
        case SLIDE_UP_PRESS_LOST_CB:
            ext->slide_up_press_lost_cb = (void(*)(void *))cb;
            break;
        case SLIDE_DOWN_PRESSING_CB:
            ext->slide_down_pressing_cb = (void(*)(void *))cb;
            break;
        case SLIDE_DOWN_PRESS_LOST_CB:
            ext->slide_down_press_lost_cb = (void(*)(void *))cb;
            break;
    }
}

lv_res_t lv_watch_obj_signal_from_element(lv_obj_t * obj, lv_signal_t sign, void * param, lv_obj_t * element)
{
    lv_res_t res = LV_RES_OK;
    lv_watch_obj_ext_t * ext = lv_obj_get_ext_attr(obj);
    if(ext == NULL) return LV_RES_INV;

    //printf("lv_watch_obj_signal, sign=%d\n", sign);
    /* Include the ancient signal function */
    lv_signal_cb_t ancient_signal = (lv_signal_cb_t)lv_watch_obj_get_free_ptr(obj);
    if(ancient_signal) {
        res = ancient_signal(obj, sign, param);
        if(res != LV_RES_OK) return res;
    }

    if(ext->slide_enable == false) {
        return res;
    }

    if(sign == LV_SIGNAL_PRESSED) {
        lv_watch_obj_pressed_handler(obj, element);
    } else if(sign == LV_SIGNAL_PRESSING) {
        lv_watch_obj_pressing_handler(obj, element);
    } else if(sign == LV_SIGNAL_RELEASED || sign == LV_SIGNAL_PRESS_LOST) {
        lv_watch_obj_press_lost_handler(obj, element);
    } else if(sign == LV_SIGNAL_CLEANUP) {
        //printf("%s signal LV_SIGNAL_CLEANUP\n", __FUNCTION__);
        lv_obj_clean_style_list(obj, LV_OBJ_PART_MAIN);
    } else if(sign ==  LV_SIGNAL_GET_TYPE) {
        lv_obj_type_t * buf = param;
        buf->type[0] = "lv_watch_obj";
    }

    return res;
}

lv_res_t lv_watch_obj_signal(lv_obj_t * obj, lv_signal_t sign, void * param)
{
    return lv_watch_obj_signal_from_element(obj, sign, param, NULL);
}

static void lv_watch_obj_enable_click(void * obj)
{
    if(!obj) return;
    lv_watch_obj_ext_t * ext = lv_obj_get_ext_attr(obj);
    if(!ext) return;
    //printf("set ext->element_click_en = 1\n");
    ext->element_click_en = 1;
}

/* It need be invoked after each callback with out delete the activity.
 * when moving ,the element(button) if forbidden clicked. afte animation finish, need recover button to be clicked.
 * ext->element_click_en = 1;
 * it must be invoked after up/down/left/right press lost callback.
*/
void lv_watch_obj_recover_element_clicked_en(lv_obj_t * obj)
{
    /* used async call to finish signal handle first */
    lv_async_call(lv_watch_obj_enable_click, obj);
}

void lv_watch_obj_recover_element_clicked_en_by_activity(lv_anim_t * a)
{
    lv_obj_t * activity_obj = a->var;
    lv_obj_t * obj = lv_watch_obj_get_obj_from_activity_obj(activity_obj);
    if(obj == NULL) return;

    lv_watch_obj_ext_t * ext = lv_obj_get_ext_attr(obj);
    if(obj == NULL) return;
    ext->element_click_en = 1;
}

void lv_watch_obj_set_left_slide_cb(lv_obj_t * obj, void (*cb)(lv_obj_t *))
{
    lv_watch_obj_set_anim_cb(obj, SLIDE_LEFT_PRESS_LOST_CB, cb);
}

void lv_watch_obj_set_right_slide_cb(lv_obj_t * obj, void (*cb)(lv_obj_t *))
{
    lv_watch_obj_set_anim_cb(obj, SLIDE_RIGHT_PRESS_LOST_CB, cb);
}

void lv_watch_obj_set_up_slide_cb(lv_obj_t * obj, void (*cb)(lv_obj_t *))
{
    lv_watch_obj_set_anim_cb(obj, SLIDE_UP_PRESS_LOST_CB, cb);
}

void lv_watch_obj_set_down_slide_cb(lv_obj_t * obj, void (*cb)(lv_obj_t *))
{
    lv_watch_obj_set_anim_cb(obj, SLIDE_DOWN_PRESS_LOST_CB, cb);
}

lv_obj_t * lv_watch_obj_get_obj_from_activity_obj(lv_obj_t * activity_obj)
{
    lv_obj_t * obj = lv_obj_get_child(activity_obj, NULL);
    while(obj) {
        if(lv_obj_get_signal_cb(obj) != lv_watch_obj_signal) obj = lv_obj_get_child(obj, NULL);
        else break;
    }

    return obj;
}

lv_obj_t * lv_watch_obj_get_activity(lv_obj_t * obj)
{

    lv_obj_t * tmp_obj = lv_obj_get_parent(obj);
    if(tmp_obj == NULL) return NULL;

    return lv_obj_get_parent(tmp_obj);
}

lv_res_t lv_watch_obj_hor_hide_action(lv_obj_t * activity_obj, lv_coord_t new_x)
{
#if LV_USE_ANIMATION
    //printf("lv_watch_obj_hor_hide_action:  new_x=%d\n", new_x);
    lv_obj_set_x_animation(activity_obj, new_x, true, LV_WATCH_OBJ_HIDE_TIME,  lv_watch_obj_hide_anim_end);
    return LV_RES_OK;
#else
    /* cleanup child tab */
    //printf("lv_watch_obj: cleanup child tab at once.\n");
    lv_watch_obj_prepare_destory(activity_obj);
    lv_obj_del(activity_obj);
    return LV_RES_INV;
#endif
}

lv_res_t lv_watch_obj_ver_hide_action(lv_obj_t * activity_obj, lv_coord_t new_y)
{
#if LV_USE_ANIMATION
    //printf("lv_watch_obj_ver_hide_action: new_y=%d\n", new_y);
    lv_obj_set_y_animation(activity_obj, new_y, true, LV_WATCH_OBJ_HIDE_TIME,  lv_watch_obj_hide_anim_end);
    return LV_RES_OK;
#else
    /* cleanup child tab */
    //printf("lv_watch_obj: cleanup child tab at once.\n");
    lv_watch_obj_prepare_destory(activity_obj);
    lv_obj_del(activity_obj);
    return LV_RES_INV;
#endif
}

/**********************
 *   STATIC FUNCTIONS
 **********************/
static lv_res_t element_signal_func(lv_obj_t * element, lv_signal_t sign, void * param)
{
    lv_res_t res;

    /* if not click signs, invoke ancient handler and return */
    if(sign != LV_SIGNAL_PRESSING && sign != LV_SIGNAL_PRESSED && sign != LV_SIGNAL_RELEASED && sign != LV_SIGNAL_PRESS_LOST) {
        /* Include the ancient signal function */
        lv_signal_cb_t sign_func = (lv_signal_cb_t)(lv_obj_get_user_data(element).user_data);
        if(!sign_func) return LV_RES_OK;
        res = sign_func(element, sign, param);
        if(res != LV_RES_OK) return res;
        return LV_RES_OK;
    }

    lv_obj_t * obj = lv_obj_get_parent(element);
    while(obj) {
        if(lv_obj_get_signal_cb(obj) != lv_watch_obj_signal) obj = lv_obj_get_parent(obj);
        else break;
    }
    if(obj == NULL) return LV_RES_INV;
    res = lv_watch_obj_signal_from_element(obj, sign, param, element);
    if(res != LV_RES_OK) return res;

    lv_watch_obj_ext_t * ext = lv_obj_get_ext_attr(obj);
    if(ext == NULL) return LV_RES_INV;
    if(!ext->element_click_en) {
        /* forbidden obj's children be clicked. */
        if(sign == LV_SIGNAL_PRESSING || sign == LV_SIGNAL_PRESSED) {
            return LV_RES_OK;
        } else if(sign == LV_SIGNAL_RELEASED) {
            /* Although lv_list and lv_btn check lv_indev_is_dragging before RELEASED action.
             * If drag slowly, lv_indev_is_dragging = false. when drag animation is working.
             * So change LV_SIGNAL_RELEASED to LV_SIGNAL_PRESS_LOST, the click action will not be invoked when dragging animation.
             * LV_SIGNAL_PRESS_LOST makes button and list state Recovered.
             * eg: if setting dragging, the pressed btn will not be high lighted*/
            lv_watch_indev_set_draged(element);
            lv_watch_obj_enable_click(obj);
            sign = LV_SIGNAL_PRESS_LOST;
        }
    }

    /* Include the ancient signal function */
    lv_signal_cb_t sign_func = (lv_signal_cb_t)(lv_obj_get_user_data(element).user_data);
    res = sign_func(element, sign, param);
    if(res != LV_RES_OK) return res;

    return LV_RES_OK;
}

static void lv_watch_obj_pressed_handler(lv_obj_t * obj, lv_obj_t * element)
{
    lv_watch_obj_ext_t * ext = lv_obj_get_ext_attr(obj);
    lv_indev_t * indev = lv_indev_get_act();
    lv_indev_get_point(indev, &ext->point_last);
    ext->point_start.x = ext->point_last.x;
    ext->point_start.y = ext->point_last.y;
    ext->point_now.x = ext->point_last.x;
    ext->point_now.y = ext->point_last.y;
    //printf("[pressed]: point_now.x=%d, point_now.y=%d, point_start.x=%d, point_start.y=%d\n",\
    //    ext->point_now.x, ext->point_now.y, ext->point_start.x, ext->point_start.y);
}

static void lv_watch_obj_pressing_handler(lv_obj_t * obj, lv_obj_t * element)
{
    lv_watch_obj_ext_t * ext = lv_obj_get_ext_attr(obj);
    lv_indev_t * indev = lv_indev_get_act();
    lv_point_t point_act;
    lv_indev_get_point(indev, &point_act);
    lv_coord_t x_diff = point_act.x - ext->point_last.x;
    lv_coord_t y_diff = point_act.y - ext->point_last.y;
    //printf("x_diff=%d, y_diff=%d\n", x_diff, y_diff);

    lv_watch_obj_element_free_ptr_client_t * ext_client = NULL;
    ext_client = element_get_ext_client(element);
    if(ext_client != NULL) {
        //printf("ext_client->sliding_down_en=%d, ext_client->sliding_up_en=%d, ext_client->sliding_left_en=%d, ext_client->sliding_right_en=%d\n", \
        //    ext_client->sliding_down_en, ext_client->sliding_up_en, ext_client->sliding_left_en, ext_client->sliding_right_en);

        if(abs(x_diff) < abs(y_diff) && !ext_client->sliding_down_en && y_diff > ext->ver_drag_start) {
            //printf("-----element ignore sliding_down signal\n");
            return;
        }

        if(abs(x_diff) < abs(y_diff) && !ext_client->sliding_up_en && y_diff < -ext->ver_drag_start) {
            //printf("-----element ignore sliding_up signal\n");
            return;
        }

        if(abs(x_diff) > abs(y_diff) && !ext_client->sliding_left_en && x_diff < -ext->hor_drag_start) {
            //printf("-----element ignore sliding_left signal\n");
            return;
        }

        if(abs(x_diff) > abs(y_diff) && !ext_client->sliding_right_en && x_diff > ext->hor_drag_start) {
            //printf("-----element ignore sliding_right signal\n");
            return;
        }
    }

    ext->point_now.x = point_act.x;
    ext->point_now.y = point_act.y;

    //printf("[pressing]: point_now.x=%d, point_now.y=%d, point_start.x=%d, point_start.y=%d\n",\
    //    ext->point_now.x, ext->point_now.y, ext->point_start.x, ext->point_start.y);
    if(ext->draging == 0) {
        if(abs(x_diff) > abs(y_diff) && abs(x_diff) > ext->hor_drag_start) {
            ext->drag_hor = 1;
            ext->draging = 1;
            /*target to disable drag for lv_page, currently set activity_obj only for a mark for feature*/
            lv_obj_set_drag(ext->activity_obj, false); //enable click, disable drag.
            //printf("set ext->drag_hor = 1\n");
            //printf("-------x_diff=%d, set element_click_en = 0\n", x_diff);
            ext->is_drag_right = (x_diff >= ext->hor_drag_start) ? 1 : 0;
        } else if(abs(y_diff) >= ext->ver_drag_start) {
            ext->drag_hor = 0;
            ext->draging = 1;
            //printf("-------y_diff=%d, set element_click_en = 0\n", y_diff);
            ext->is_drag_up = (y_diff >= ext->ver_drag_start) ? 0 : 1;
        }
    }

    /* hor silding show limit */
    if(ext->drag_hor) {
        /* hor sliding*/
        if(!ext->is_drag_right && ext->slide_left_pressing_cb) {
            ext->slide_left_pressing_cb(obj);
        } else if(ext->is_drag_right && ext->slide_right_pressing_cb) {
            ext->slide_right_pressing_cb(obj);
        }
    } else if(ext->draging) {
        /* ver sliding*/
        if(!ext->is_drag_up && ext->slide_down_pressing_cb) {
            ext->slide_down_pressing_cb(obj);
        } else if(ext->is_drag_up && ext->slide_up_pressing_cb) {
            ext->slide_up_pressing_cb(obj);
        }
    }

    ext->point_last.x = point_act.x;
    ext->point_last.y = point_act.y;

    /* if gesture begined, disable click */
    if(indev->proc.types.pointer.gesture_sent) ext->element_click_en = 0;
}

static void lv_watch_obj_press_lost_handler(lv_obj_t * obj, lv_obj_t * element)
{
    lv_watch_obj_ext_t * ext = lv_obj_get_ext_attr(obj);

    /* if count acceleration must after drag set recover. */
    /* hor slide : slide up/down callback*/
    /*ext->element_click_en = 1, must invoked after animation finished. */
    if(ext->drag_hor) {
        /* hor sliding*/
        if(!ext->is_drag_right && ext->slide_left_press_lost_cb) {
            ext->slide_left_press_lost_cb(obj);
        } else if(ext->is_drag_right && ext->slide_right_press_lost_cb) {
            ext->slide_right_press_lost_cb(obj);
        }
    } else if(ext->draging) {
        /* ver sliding*/
        if(!ext->is_drag_up && ext->slide_down_press_lost_cb) {
            ext->slide_down_press_lost_cb(obj);
        } else if(ext->is_drag_up && ext->slide_up_press_lost_cb) {
            ext->slide_up_press_lost_cb(obj);
        }
    }

    ext->drag_hor = 0;
    ext->draging = 0;

    lv_obj_set_drag(ext->activity_obj, true);
}

static void lv_watch_obj_hide_anim_end(lv_anim_t * a)
{
    // printf("lv_watch_obj_hide_anim_end: cleanup obj and its child\n");
    /* recover when receive cleanup signal */
    lv_obj_t * activity_obj = a->var;
    lv_watch_obj_prepare_destory(activity_obj);
    if(activity_obj) lv_obj_del(activity_obj);
}

/* lv_watch_obj_png_release(lv_scr_act()) can not invoked when animation, may segment fault.
 * eg: settings/languash/shutdown can free all pngs before delete activity.
 * So whether free png cache, it depends on the activity's prepare_destory.
 * If an activity need be deleted sync with another activity. It can be coded in prepare_destory.
 */
static void lv_watch_obj_prepare_destory(lv_obj_t * activity_obj)
{
    lv_watch_activity_ext_t * activity_ext = lv_obj_get_ext_attr(activity_obj);
    LV_ASSERT_MEM(activity_ext);
    if(activity_ext == NULL) return;

    if(activity_ext->prepare_destory)  activity_ext->prepare_destory(activity_obj);
}

static lv_watch_obj_element_free_ptr_client_t * element_get_ext_client(lv_obj_t * element)
{
    if(element == NULL) return NULL;
    return lv_obj_get_user_data(element).user_data_for_client;
}

static void default_right_pressing(lv_obj_t  * obj)
{
    if(lv_obj_get_signal_cb(obj) != lv_watch_obj_signal) {
        printf("default_right_press_lost: obj is not watch_obj\n");
        return;
    }

    lv_obj_t * act_obj = lv_watch_obj_get_activity(obj);
    if(act_obj == NULL) return;

    lv_watch_obj_ext_t * ext = lv_obj_get_ext_attr(obj);
    if(ext == NULL) return;

    //printf("move [obj]: point_now.x=%d, point_now.y=%d, point_start.x=%d, point_start.y=%d\n",\
    //    ext->point_now.x, ext->point_now.y, ext->point_start.x, ext->point_start.y);

    lv_coord_t new_x = 0 +  ext->point_now.x - ext->point_start.x;
    new_x = (new_x > LV_HOR_RES) ? LV_HOR_RES : new_x;
    new_x = (new_x < 0) ? 0 : new_x;
    lv_obj_set_x(act_obj, new_x);
}

static void default_right_press_lost(lv_obj_t  * obj)
{
    if(lv_obj_get_signal_cb(obj) != lv_watch_obj_signal) {
        printf("default_right_press_lost: obj is not watch_obj\n");
        return;
    }
    lv_obj_t * act_obj = lv_watch_obj_get_activity(obj);
    if(act_obj == NULL) return;

    lv_watch_obj_ext_t * ext = lv_obj_get_ext_attr(obj);
    if(ext == NULL) return;

    // add acceleration
    lv_indev_t * indev = lv_indev_get_act();
    lv_point_t point_act;
    lv_indev_get_point(indev, &point_act);
    lv_point_t vect;
    lv_indev_get_vect(indev, &vect);
    lv_coord_t x_predict = 0;

    while(vect.x != 0)   {
        x_predict += vect.x;
        vect.x = vect.x * (100 - LV_INDEV_DEF_DRAG_THROW) / 100;
    }

    lv_coord_t x = lv_obj_get_x(act_obj);

    //printf("old_x=%d, x_predict=%d\n", x, x_predict);
    if(x_predict < 0) {
        x = x + x_predict;
    }
    //printf("new_x=%d, x_predict=%d\n", x, x_predict);

    if(x > LV_WATCH_OBJ_HIDE_THRESHOLD) {
        lv_watch_obj_hor_hide_action(act_obj, LV_HOR_RES);
    } else {
        lv_obj_set_x_animation(act_obj, 0, true, LV_WATCH_OBJ_HIDE_TIME, lv_watch_obj_recover_element_clicked_en_by_activity);
    }
}

static void default_up_pressing(lv_obj_t  * obj)
{
    if(lv_obj_get_signal_cb(obj) != lv_watch_obj_signal) {
        printf("default_right_press_lost: obj is not watch_obj\n");
        return;
    }
    lv_obj_t * act_obj = lv_watch_obj_get_activity(obj);
    if(act_obj == NULL) return;

    lv_watch_obj_ext_t * ext = lv_obj_get_ext_attr(obj);
    if(ext == NULL) return;

    //printf("move [obj]: point_now.x=%d, point_now.y=%d, point_start.x=%d, point_start.y=%d\n",\
    //    ext->point_now.x, ext->point_now.y, ext->point_start.x, ext->point_start.y);

    lv_coord_t new_y = 0 +  ext->point_now.y - ext->point_start.y;
    new_y = (new_y < -LV_VER_RES) ? -LV_VER_RES : new_y;
    new_y = (new_y > 0) ? 0 : new_y;
    lv_obj_set_y(act_obj, new_y);
}

static void default_up_press_lost(lv_obj_t  * obj)
{
    if(lv_obj_get_signal_cb(obj) != lv_watch_obj_signal) {
        printf("default_right_press_lost: obj is not watch_obj\n");
        return;
    }
    lv_obj_t * act_obj = lv_watch_obj_get_activity(obj);
    if(act_obj == NULL) return;

    lv_watch_obj_ext_t * ext = lv_obj_get_ext_attr(obj);
    if(ext == NULL) return;

    // add acceleration
    lv_indev_t * indev = lv_indev_get_act();
    lv_point_t point_act;
    lv_indev_get_point(indev, &point_act);
    lv_point_t vect;
    lv_indev_get_vect(indev, &vect);
    lv_coord_t y_predict = 0;

    while(vect.y != 0)   {
        y_predict += vect.y;
        vect.y = vect.y * (100 - LV_INDEV_DEF_DRAG_THROW) / 100;
    }

    lv_coord_t y = lv_obj_get_y(act_obj);

    //printf("old_y=%d\n", y);
    y = y + y_predict;
    //printf("y_predict=%d, new_y=%d\n", y_predict, y);

    if(y < -LV_WATCH_OBJ_HIDE_THRESHOLD) {
        lv_watch_obj_ver_hide_action(act_obj, -LV_VER_RES);
    } else {
        lv_obj_set_y_animation(act_obj, 0, true, LV_WATCH_OBJ_HIDE_TIME, lv_watch_obj_recover_element_clicked_en_by_activity);
    }
}

static void set_default_anim_callback(lv_obj_t * obj)
{
    lv_watch_obj_reset_anim_cb(obj);

    lv_watch_obj_ext_t * ext = lv_obj_get_ext_attr(obj);
    if(ext == NULL) return;

    if(ext->anim_mode == LV_WATCH_ANIM_HOR_RIGHT_HIDE) {
        lv_watch_obj_set_anim_cb(obj, SLIDE_RIGHT_PRESSING_CB, default_right_pressing);
        lv_watch_obj_set_anim_cb(obj, SLIDE_RIGHT_PRESS_LOST_CB, default_right_press_lost);
    } else if(ext->anim_mode == LV_WATCH_ANIM_VER_UP_HIDE) {
        lv_watch_obj_set_anim_cb(obj, SLIDE_UP_PRESSING_CB, default_up_pressing);
        lv_watch_obj_set_anim_cb(obj, SLIDE_UP_PRESS_LOST_CB, default_up_press_lost);
    } else if(ext->anim_mode == LV_WATCH_LEFT_OR_RIGHT_SLIDE_CALLBACK) {
        return;
    } else {
        //LV_WATCH_ANIM_NO
        return;
    }
}

#endif /*USE_LV_WATCH_OBJ*/
